package permission;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import permission.role.AdministratorRole;
import permission.role.CourseLeaderRole;
import permission.role.OwnerRole;
import permission.role.Role;
import permission.role.Role.RoleType;
import permission.role.TutorRole;
import course.Course;


public enum RoleManager {
	INSTANCE;
	
	public boolean isAccessGranted(Secureable objectToSecure, Roleable objectWantsAccess) {
		return isAdministrator(objectWantsAccess) || isOwner(objectToSecure, objectWantsAccess) || isPartialAccessGranted(objectToSecure, objectWantsAccess);
	}
	
	public boolean isPartialAccessGranted(Secureable objectToSecure, Roleable objectWantsAccess) {
		if(objectToSecure == null || objectWantsAccess == null) {
			return false;
		}
		Collection<Role> objectRoles = objectToSecure.getAccessRoles();
		
		for(Role accRole : objectWantsAccess.getRoles()) {
			if(RoleType.ADMINISTRATOR.equals(accRole.getRoleType()) || RoleType.OWNER.equals(accRole)) {
				continue;
			}
			if(objectRoles.contains(accRole)) {
				return true;
			}
		}
		return false;
	}
	
	public List<CourseLeaderRole> getCourseLeaderRoles(Secureable objectToSecure, Roleable objectWantsAccess) {
		return getRolesForType(CourseLeaderRole.class, RoleType.COURSE_LEADER, objectToSecure, objectWantsAccess);
	}
	
	public List<TutorRole> getTutorRoles(Secureable objectToSecure, Roleable objectWantsAccess) {
		return getRolesForType(TutorRole.class, RoleType.TUTOR, objectToSecure, objectWantsAccess);
	}
	
	private <T extends Role> List<T> getRolesForType(Class<T> clazz, RoleType type, Secureable objectToSecure, Roleable objectWantsAccess) {
		List<T> roles = new ArrayList<T>();
		if(objectToSecure == null || objectWantsAccess == null) {
			return roles;
		}
		
		for(Role r : objectToSecure.getAccessRoles()) {
			if(!type.equals(r.getRoleType())) {
				continue;
			}
			@SuppressWarnings("unchecked")
			T role = (T)r;
			if(objectWantsAccess.getRoles().contains(role)) {
				roles.add(role);
			}
		}
		return roles;
	}
	
	public boolean isCourseLeader(Course course, Roleable objectWantsAccess) {
		if(course == null || objectWantsAccess == null) {
			return false;
		}
		
		CourseLeaderRole clr = new CourseLeaderRole(course);
		return objectWantsAccess.getRoles().contains(clr);
	}
	
	public boolean isAdministrator(Roleable objectWantsAccess) {
		if(objectWantsAccess == null) {
			return false;
		}
		return objectWantsAccess.getRoles().contains(new AdministratorRole());
	}
	
	public boolean isOwner(Secureable objectToSecure, Roleable objectWantsAccess) {
		if(objectToSecure == null || objectWantsAccess == null) {
			return false;
		}
		Collection<Role> acc = objectWantsAccess.getRoles();
		
		List<Boolean> isOwner = new ArrayList<Boolean>();
		
		for(Role role : acc) {
			if(!RoleType.OWNER.equals(role.getRoleType())) {
				continue;
			}
			OwnerRole r = (OwnerRole)role;
			isOwner.add(objectToSecure.equals(r.getObjectOfOwnerShip()));
		}
		return disjunctive(isOwner);
	}
	
	private boolean disjunctive(List<Boolean> boolList) {
		for(Boolean b : boolList) {
			if(b.booleanValue()) {
				return true;
			}
		}
		return false;
	}
}
